Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finite Monotonic #153

Closed
wants to merge 7 commits into from
Closed

Finite Monotonic #153

wants to merge 7 commits into from

Conversation

lemmy
Copy link
Member

@lemmy lemmy commented Oct 17, 2024

Related to #97

  • Violations of Convergence are missed without GarbageCollect (regardless of state constraint or conjunct to disable Increment)
  • Conjunct to disable Increment causes spurious liveness violation that ends in stuttering (see bottom)
-> % tlc -note SCCRDT.tla -config SCCRDT.tla                     
TLC2 Version 2.20 of Day Month 20?? (rev: 2360829)
Running breadth-first search Model-Checking with fp 55 and seed 1802278425399457922 with 1 worker on 10 cores with 7282MB heap and 64MB offheap memory [pid: 41282] (Mac OS X 15.0.1 aarch64, Homebrew 11.0.24 x86_64, MSBDiskFPSet, DiskStateQueue).
Parsing file /Users/markus/src/TLA/_specs/examples/specifications/FiniteMonotonic/SCCRDT.tla
Parsing file /private/var/folders/7d/4x6z2cc91jl588ysynlc1tfc0000gn/T/tlc-11431439590268313249/TLC.tla (jar:file:/Applications/TLA+%20Toolbox.app/Contents/Eclipse/tla2tools.jar!/tla2sany/StandardModules/TLC.tla)
Parsing file /private/var/folders/7d/4x6z2cc91jl588ysynlc1tfc0000gn/T/tlc-11431439590268313249/Naturals.tla (jar:file:/Applications/TLA+%20Toolbox.app/Contents/Eclipse/tla2tools.jar!/tla2sany/StandardModules/Naturals.tla)
Parsing file /private/var/folders/7d/4x6z2cc91jl588ysynlc1tfc0000gn/T/tlc-11431439590268313249/Sequences.tla (jar:file:/Applications/TLA+%20Toolbox.app/Contents/Eclipse/tla2tools.jar!/tla2sany/StandardModules/Sequences.tla)
Parsing file /private/var/folders/7d/4x6z2cc91jl588ysynlc1tfc0000gn/T/tlc-11431439590268313249/IOUtils.tla (jar:file:/Applications/TLA+%20Toolbox.app/Contents/Eclipse/CommunityModules-deps.jar!/IOUtils.tla)
Parsing file /private/var/folders/7d/4x6z2cc91jl588ysynlc1tfc0000gn/T/tlc-11431439590268313249/FiniteSets.tla (jar:file:/Applications/TLA+%20Toolbox.app/Contents/Eclipse/tla2tools.jar!/tla2sany/StandardModules/FiniteSets.tla)
Parsing file /private/var/folders/7d/4x6z2cc91jl588ysynlc1tfc0000gn/T/tlc-11431439590268313249/Integers.tla (jar:file:/Applications/TLA+%20Toolbox.app/Contents/Eclipse/tla2tools.jar!/tla2sany/StandardModules/Integers.tla)
Semantic processing of module Naturals
Semantic processing of module Sequences
Semantic processing of module FiniteSets
Semantic processing of module TLC
Semantic processing of module Integers
Semantic processing of module IOUtils
Semantic processing of module SCCRDT
Starting... (2024-10-17 06:45:34)
<<"conf", [D |-> 0, F |-> 0, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 0, F |-> 0, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 0, F |-> 0, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 0, F |-> 0, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 0, F |-> 1, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 0, F |-> 1, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 0, F |-> 1, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 0, F |-> 1, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 0, F |-> 2, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 0, F |-> 2, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 0, F |-> 2, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 0, F |-> 2, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 0, F |-> 3, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 0, F |-> 3, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 0, F |-> 3, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 0, F |-> 3, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 0, F |-> 4, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 0, F |-> 4, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 0, F |-> 4, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 0, F |-> 4, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 0, F |-> 5, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 0, F |-> 5, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 0, F |-> 5, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 0, F |-> 5, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 0, F |-> 6, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 0, F |-> 6, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 0, F |-> 6, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 0, F |-> 6, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 1, F |-> 0, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 1, F |-> 0, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 1, F |-> 0, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 1, F |-> 0, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 1, F |-> 1, G |-> FALSE, C |-> FALSE], 13>>
<<"conf", [D |-> 1, F |-> 1, G |-> FALSE, C |-> TRUE], 13>>
<<"conf", [D |-> 1, F |-> 1, G |-> TRUE, C |-> FALSE], 13>>
<<"conf", [D |-> 1, F |-> 1, G |-> TRUE, C |-> TRUE], 13>>
<<"conf", [D |-> 1, F |-> 2, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 1, F |-> 2, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 1, F |-> 2, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 1, F |-> 2, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 1, F |-> 3, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 1, F |-> 3, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 1, F |-> 3, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 1, F |-> 3, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 1, F |-> 4, G |-> FALSE, C |-> FALSE], 13>>
<<"conf", [D |-> 1, F |-> 4, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 1, F |-> 4, G |-> TRUE, C |-> FALSE], 13>>
<<"conf", [D |-> 1, F |-> 4, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 1, F |-> 5, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 1, F |-> 5, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 1, F |-> 5, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 1, F |-> 5, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 1, F |-> 6, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 1, F |-> 6, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 1, F |-> 6, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 1, F |-> 6, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 2, F |-> 0, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 2, F |-> 0, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 2, F |-> 0, G |-> TRUE, C |-> FALSE], 13>>
<<"conf", [D |-> 2, F |-> 0, G |-> TRUE, C |-> TRUE], 13>>
<<"conf", [D |-> 2, F |-> 1, G |-> FALSE, C |-> FALSE], 13>>
<<"conf", [D |-> 2, F |-> 1, G |-> FALSE, C |-> TRUE], 13>>
<<"conf", [D |-> 2, F |-> 1, G |-> TRUE, C |-> FALSE], 13>>
<<"conf", [D |-> 2, F |-> 1, G |-> TRUE, C |-> TRUE], 13>>
<<"conf", [D |-> 2, F |-> 2, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 2, F |-> 2, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 2, F |-> 2, G |-> TRUE, C |-> FALSE], 13>>
<<"conf", [D |-> 2, F |-> 2, G |-> TRUE, C |-> TRUE], 13>>
<<"conf", [D |-> 2, F |-> 3, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 2, F |-> 3, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 2, F |-> 3, G |-> TRUE, C |-> FALSE], 13>>
<<"conf", [D |-> 2, F |-> 3, G |-> TRUE, C |-> TRUE], 13>>
<<"conf", [D |-> 2, F |-> 4, G |-> FALSE, C |-> FALSE], 13>>
<<"conf", [D |-> 2, F |-> 4, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 2, F |-> 4, G |-> TRUE, C |-> FALSE], 13>>
<<"conf", [D |-> 2, F |-> 4, G |-> TRUE, C |-> TRUE], 13>>
<<"conf", [D |-> 2, F |-> 5, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 2, F |-> 5, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 2, F |-> 5, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 2, F |-> 5, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 2, F |-> 6, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 2, F |-> 6, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 2, F |-> 6, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 2, F |-> 6, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 3, F |-> 0, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 3, F |-> 0, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 3, F |-> 0, G |-> TRUE, C |-> FALSE], 13>>
<<"conf", [D |-> 3, F |-> 0, G |-> TRUE, C |-> TRUE], 13>>
<<"conf", [D |-> 3, F |-> 1, G |-> FALSE, C |-> FALSE], 13>>
<<"conf", [D |-> 3, F |-> 1, G |-> FALSE, C |-> TRUE], 13>>
<<"conf", [D |-> 3, F |-> 1, G |-> TRUE, C |-> FALSE], 13>>
<<"conf", [D |-> 3, F |-> 1, G |-> TRUE, C |-> TRUE], 13>>
<<"conf", [D |-> 3, F |-> 2, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 3, F |-> 2, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 3, F |-> 2, G |-> TRUE, C |-> FALSE], 13>>
<<"conf", [D |-> 3, F |-> 2, G |-> TRUE, C |-> TRUE], 13>>
<<"conf", [D |-> 3, F |-> 3, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 3, F |-> 3, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 3, F |-> 3, G |-> TRUE, C |-> FALSE], 13>>
<<"conf", [D |-> 3, F |-> 3, G |-> TRUE, C |-> TRUE], 13>>
<<"conf", [D |-> 3, F |-> 4, G |-> FALSE, C |-> FALSE], 13>>
<<"conf", [D |-> 3, F |-> 4, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 3, F |-> 4, G |-> TRUE, C |-> FALSE], 13>>
<<"conf", [D |-> 3, F |-> 4, G |-> TRUE, C |-> TRUE], 13>>
<<"conf", [D |-> 3, F |-> 5, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 3, F |-> 5, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 3, F |-> 5, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 3, F |-> 5, G |-> TRUE, C |-> TRUE], 0>>
<<"conf", [D |-> 3, F |-> 6, G |-> FALSE, C |-> FALSE], 0>>
<<"conf", [D |-> 3, F |-> 6, G |-> FALSE, C |-> TRUE], 0>>
<<"conf", [D |-> 3, F |-> 6, G |-> TRUE, C |-> FALSE], 0>>
<<"conf", [D |-> 3, F |-> 6, G |-> TRUE, C |-> TRUE], 0>>
Computing initial states...
Finished computing initial states: 0 distinct states generated at 2024-10-17 06:47:24.
Model checking completed. No error has been found.
  Estimates of the probability that TLC did not check all reachable states
  because two distinct states had the same fingerprint:
  calculated (optimistic):  val = 0.0
0 states generated, 0 distinct states found, 0 states left on queue.
The depth of the complete state graph search is 0.
Finished in 01min 50s at (2024-10-17 06:47:24)

Better exit values: tlaplus/tlaplus#1041

-> % D=3 F=4 G=TRUE C=FALSE tlc -note MCCRDT.tla -config MCCRDT.cfg
TLC2 Version 2.20 of Day Month 20?? (rev: 2360829)
Running breadth-first search Model-Checking with fp 130 and seed 4584290592961789553 with 1 worker on 10 cores with 7282MB heap and 64MB offheap memory [pid: 42208] (Mac OS X 15.0.1 aarch64, Homebrew 11.0.24 x86_64, MSBDiskFPSet, DiskStateQueue).
Parsing file /Users/markus/src/TLA/_specs/examples/specifications/FiniteMonotonic/MCCRDT.tla
Parsing file /Users/markus/src/TLA/_specs/examples/specifications/FiniteMonotonic/CRDT.tla
Parsing file /private/var/folders/7d/4x6z2cc91jl588ysynlc1tfc0000gn/T/tlc-14565095515634353915/IOUtils.tla (jar:file:/Applications/TLA+%20Toolbox.app/Contents/Eclipse/CommunityModules-deps.jar!/IOUtils.tla)
Parsing file /private/var/folders/7d/4x6z2cc91jl588ysynlc1tfc0000gn/T/tlc-14565095515634353915/TLC.tla (jar:file:/Applications/TLA+%20Toolbox.app/Contents/Eclipse/tla2tools.jar!/tla2sany/StandardModules/TLC.tla)
Parsing file /private/var/folders/7d/4x6z2cc91jl588ysynlc1tfc0000gn/T/tlc-14565095515634353915/Naturals.tla (jar:file:/Applications/TLA+%20Toolbox.app/Contents/Eclipse/tla2tools.jar!/tla2sany/StandardModules/Naturals.tla)
Parsing file /private/var/folders/7d/4x6z2cc91jl588ysynlc1tfc0000gn/T/tlc-14565095515634353915/Integers.tla (jar:file:/Applications/TLA+%20Toolbox.app/Contents/Eclipse/tla2tools.jar!/tla2sany/StandardModules/Integers.tla)
Parsing file /private/var/folders/7d/4x6z2cc91jl588ysynlc1tfc0000gn/T/tlc-14565095515634353915/Sequences.tla (jar:file:/Applications/TLA+%20Toolbox.app/Contents/Eclipse/tla2tools.jar!/tla2sany/StandardModules/Sequences.tla)
Parsing file /private/var/folders/7d/4x6z2cc91jl588ysynlc1tfc0000gn/T/tlc-14565095515634353915/FiniteSets.tla (jar:file:/Applications/TLA+%20Toolbox.app/Contents/Eclipse/tla2tools.jar!/tla2sany/StandardModules/FiniteSets.tla)
Semantic processing of module Naturals
Semantic processing of module CRDT
Semantic processing of module Sequences
Semantic processing of module FiniteSets
Semantic processing of module TLC
Semantic processing of module Integers
Semantic processing of module IOUtils
Semantic processing of module MCCRDT
Starting... (2024-10-17 06:52:34)
Implied-temporal checking--satisfiability problem has 1 branches.
Computing initial states...
Finished computing initial states: 1 distinct state generated at 2024-10-17 06:52:34.
Progress(9) at 2024-10-17 06:52:34: 621 states generated, 100 distinct states found, 0 states left on queue.
Checking temporal properties for the complete state space with 100 total distinct states at (2024-10-17 06:52:34)
Error: Temporal properties were violated.

Error: The following behavior constitutes a counter-example:

State 1: <Initial predicate>
counter = (n1 :> (n1 :> 0 @@ n2 :> 0) @@ n2 :> (n1 :> 0 @@ n2 :> 0))

State 2: <S!Next line 34, col 1 to line 34, col 18 of module MCCRDT>
counter = (n1 :> (n1 :> 1 @@ n2 :> 0) @@ n2 :> (n1 :> 0 @@ n2 :> 0))

State 3: <S!Next line 34, col 1 to line 34, col 18 of module MCCRDT>
counter = (n1 :> (n1 :> 1 @@ n2 :> 0) @@ n2 :> (n1 :> 0 @@ n2 :> 1))

State 4: <S!Next line 34, col 1 to line 34, col 18 of module MCCRDT>
counter = (n1 :> (n1 :> 1 @@ n2 :> 0) @@ n2 :> (n1 :> 0 @@ n2 :> 2))

State 5: <S!Next line 34, col 1 to line 34, col 18 of module MCCRDT>
counter = (n1 :> (n1 :> 1 @@ n2 :> 0) @@ n2 :> (n1 :> 0 @@ n2 :> 3))

State 6: <S!Next line 34, col 1 to line 34, col 18 of module MCCRDT>
counter = (n1 :> (n1 :> 2 @@ n2 :> 0) @@ n2 :> (n1 :> 0 @@ n2 :> 3))

State 7: <S!Next line 34, col 1 to line 34, col 18 of module MCCRDT>
counter = (n1 :> (n1 :> 3 @@ n2 :> 0) @@ n2 :> (n1 :> 0 @@ n2 :> 3))

State 8: <S!Next line 34, col 1 to line 34, col 18 of module MCCRDT>
counter = (n1 :> (n1 :> 3 @@ n2 :> 3) @@ n2 :> (n1 :> 0 @@ n2 :> 3))

State 9: Stuttering
Finished checking temporal properties in 00s at 2024-10-17 06:52:34
621 states generated, 100 distinct states found, 0 states left on queue.
The depth of the complete state graph search is 9.
Finished in 00s at (2024-10-17 06:52:34)

lemmy added 3 commits October 17, 2024 06:43
…e initial states into a liveness property.

Signed-off-by: Markus Alexander Kuppe <[email protected]>
… only finitely many Increment actions. A sufficient but weaker fairness constraint would be one that guarantees a sufficient number of uninterrupted Gossip steps.

Signed-off-by: Markus Alexander Kuppe <[email protected]>
…th and without the `GarbageCollect` action, a state constraint or a conjunct to disable `Increment`, and different bounds for the divergence.

Signed-off-by: Markus Alexander Kuppe <[email protected]>
@lemmy

This comment was marked as duplicate.

lemmy added 4 commits October 18, 2024 11:18
…ned to `Next.` Instead, toggle between `GarbageCollect` conjoined to `Next` and `GarbageCollect` as a separate VIEW.

Signed-off-by: Markus Alexander Kuppe <[email protected]>
…to a CSV file is violated.

Depends on pending PR tlaplus/tlaplus#1042

Signed-off-by: Markus Alexander Kuppe <[email protected]>
a successful run with no counterexample, a run that detected
a liveness violation, or a postcondition violation.

Signed-off-by: Markus Alexander Kuppe <[email protected]>
@lemmy
Copy link
Member Author

lemmy commented Jan 6, 2025

@muenchnerkindl What's your feeling? Can TLAPS (with the enablement branch) prove the liveness property Convergence given the specified fairness constraint?

https://github.com/tlaplus/Examples/blob/3debf309d1d1afe33baca0f20f93821737b0307c/specifications/FiniteMonotonic/CRDT.tla

@muenchnerkindl
Copy link
Collaborator

muenchnerkindl commented Jan 7, 2025

Interesting challenge! I believe that TLAPS should be able to prove the property but I think the specification is not the one you want. Since <<Increment(n)>>_vars is always enabled, so is <<Next>>_vars, and therefore the spec implies that there must be infinitely many non-stuttering steps. On the other hand the conjunct \A n,o \in Node : <>[][Gossip(n,o)]_vars implies eventual stuttering, so the spec is just equivalent to FALSE. (Ah, yes, the joy of not machine-closed specifications!) I think you want the fairness condition

/\ \A n,o \in Node : WF_vars(Gossip(n,o))
/\ <>[][\E n,o \in Node : Gossip(n,o)]_vars

(Alternatively, you can leave the second conjunct as it is, but I find the above a little easier to understand.)

I'll try to find some time to prove the property for the modified spec.

@lemmy
Copy link
Member Author

lemmy commented Feb 11, 2025

@muenchnerkindl wrote the following TLAPS proof:

------------------------------- MODULE CRDT ---------------------------------

EXTENDS Naturals, FiniteSets, NaturalsInduction, TLAPS

CONSTANT Node
ASSUME NodeAssumption == IsFiniteSet(Node)

VARIABLE counter
vars == counter

TypeOK == counter \in [Node -> [Node -> Nat]]

Safety == \A n, o \in Node : counter[n][n] >= counter[o][n]

Monotonic == \A n, o \in Node : counter'[n][o] >= counter[n][o]

Monotonicity == [][Monotonic]_counter

Convergence == []<>(\A n, o \in Node : counter[n] = counter[o])

Init == counter = [n \in Node |-> [o \in Node |-> 0]]

Increment(n) == counter' = [counter EXCEPT ![n][n] = @ + 1]

Gossip(n, o) ==
  LET Max(a, b) == IF a > b THEN a ELSE b IN
  counter' = [
    counter EXCEPT ![o] = [
      nodeView \in Node |->
        Max(counter[n][nodeView], counter[o][nodeView])
      ]
    ]

Next ==
  \/ \E n \in Node : Increment(n)
  \/ \E n, o \in Node : Gossip(n, o)

Spec ==
  /\ Init
  /\ [][Next]_counter

-----------------------------------------------------------------------------
(***************************************************************************)
(* Proofs of safety properties.                                            *)
(***************************************************************************)

THEOREM TypeCorrect == Spec => []TypeOK
<1>1. Init => TypeOK
  BY DEF Init, TypeOK
<1>2. TypeOK /\ [Next]_vars => TypeOK'
  BY DEF TypeOK, Next, Increment, Gossip, vars
<1>. QED  BY <1>1, <1>2, PTL DEF Spec, vars


THEOREM Safe == Spec => []Safety
<1>1. Init => Safety
  BY DEF Init, Safety
<1>2. TypeOK /\ Safety /\ [Next]_vars => Safety'
  BY DEF TypeOK, Safety, Next, Increment, Gossip, vars
<1>. QED  BY <1>1, <1>2, TypeCorrect, PTL DEF Spec, vars

THEOREM Spec => Monotonicity
<1>1. TypeOK /\ [Next]_vars => [Monotonic]_vars
  BY DEF TypeOK, Safety, Next, Increment, Gossip, vars, Monotonic
<1>. QED  BY <1>1, TypeCorrect, PTL DEF Spec, Monotonicity, vars

-----------------------------------------------------------------------------
(***************************************************************************)
(* Fairness and liveness assumptions.                                      *)
(* We assume that Gossip actions will eventually occur when enabled, and   *)
(* that from some point onwards, only Gossip actions will be performed.    *)
(* In other words, incrementation of counters happens only finitely often. *)
(* Note that the second conjunct is not a standard fairness condition,     *)
(* yet the overall specification is machine closed.                        *)
(***************************************************************************)
Fairness ==
    /\ \A n, o \in Node : WF_vars(Gossip(n,o))
    /\ <>[][\E n, o \in Node : Gossip(n,o)]_vars

FairSpec ==
  /\ Spec
  /\ Fairness  

-----------------------------------------------------------------------------
(***************************************************************************)
(* Auxiliary definitions in preparation for the liveness proof.            *)
(* Sum the values of a vector of natural numbers indexed by Node.          *)
(* This operator could be defined using a Fold, but since there is no      *)
(* library of theorems about Fold, we define it directly from scratch.     *)
(* We then state a few facts about Sum, without proof.                     *)
(***************************************************************************)
Sum(f) ==
  LET SumS[S \in SUBSET Node] == 
          IF S = {} THEN 0
          ELSE LET x == CHOOSE x \in S : TRUE
               IN  f[x] + SumS[S \ {x}]
  IN  SumS[Node]

LEMMA SumType ==
  ASSUME NEW f \in [Node -> Nat]
  PROVE  Sum(f) \in Nat
PROOF OMITTED

LEMMA SumIsZero ==
  ASSUME NEW f \in [Node -> Nat]
  PROVE  Sum(f) = 0 <=> \A x \in Node : f[x] = 0
PROOF OMITTED

LEMMA SumWeaklyMonotonic ==
  ASSUME NEW f \in [Node -> Nat], NEW g \in [Node -> Nat],
         \A x \in Node : f[x] <= g[x]
  PROVE  Sum(f) <= Sum(g)
PROOF OMITTED

LEMMA SumStronglyMonotonic ==
  ASSUME NEW f \in [Node -> Nat], NEW g \in [Node -> Nat],
         \A x \in Node : f[x] <= g[x],
         \E x \in Node : f[x] < g[x]
  PROVE  Sum(f) < Sum(g)
PROOF OMITTED

-----------------------------------------------------------------------------
(***************************************************************************)
(* Proof of the convergence property for the specification with fairness.  *)
(***************************************************************************)

\* First prove when <<Gossip(n,o)>>_vars is enabled.

LEMMA EnabledGossip ==
  ASSUME NEW n \in Node, NEW o \in Node, TypeOK
  PROVE  (ENABLED <<Gossip(n,o)>>_vars) <=>
         \E v \in Node : counter[o][v] < counter[n][v]
<1>. USE DEF TypeOK
<1>1. ASSUME ENABLED <<Gossip(n,o)>>_vars
             PROVE   \E v \in Node : counter[o][v] < counter[n][v]
  <2>. CASE <<Gossip(n,o)>>_counter
    BY DEF Gossip
  <2>. QED  BY <1>1, ExpandENABLED DEF Gossip, vars     
<1>2. ASSUME NEW v \in Node, counter[o][v] < counter[n][v]
      PROVE  ENABLED <<Gossip(n,o)>>_vars
  <2>. DEFINE Max(a, b) == IF a > b THEN a ELSE b
              ctr == [counter EXCEPT ![o] =
                        [nv \in Node |-> Max(counter[n][nv], counter[o][nv])]]
  <2>. ctr[o][v] # counter[o][v]
    BY <1>2
  <2>. QED  BY ExpandENABLED, Zenon DEF Gossip, vars
<1>. QED  BY <1>1, <1>2

(***************************************************************************)
(* Definition of the termination measure.                                  *)
(* Distance(o) sums the differences between node o's knowledge of the      *)
(* counters of other nodes and their true values.                          *)
(* Measure sums Distance(o), for all nodes o.                              *)
(* We prove elementary facts about the termination measure and in          *)
(* particular show how the Gossip action interacts with it.                *)
(***************************************************************************)
DistFun(o) == [n \in Node |-> counter[n][n] - counter[o][n]]

Distance(o) == Sum(DistFun(o))

Measure == Sum([o \in Node |-> Distance(o)])

LEMMA MeasureType ==
  ASSUME TypeOK, Safety
  PROVE  /\ \A o \in Node : DistFun(o) \in [Node -> Nat]
         /\ \A o \in Node : Distance(o) \in Nat
         /\ Measure \in Nat
<1>. ASSUME NEW o \in Node
     PROVE  DistFun(o) \in [Node -> Nat]
  BY DEF TypeOK, Safety, DistFun
<1>. QED  BY SumType, Zenon DEF Distance, Measure

\* We need a copy of the above theorem where all variables are primed.
\* One could derive this from MeasureType using PTL, but we just copy
\* and paste the proof.
LEMMA MeasureTypePrime ==
  ASSUME TypeOK', Safety'
  PROVE  /\ \A o \in Node : DistFun(o)' \in [Node -> Nat]
         /\ \A o \in Node : Distance(o)' \in Nat
         /\ Measure' \in Nat
<1>. ASSUME NEW o \in Node
     PROVE  DistFun(o)' \in [Node -> Nat]
  BY DEF TypeOK, Safety, DistFun
<1>. QED  BY SumType, Zenon DEF Distance, Measure

\* The termination measure is zero iff all nodes agree on the 
\* counter values of all nodes.
LEMMA MeasureIsZero ==
  ASSUME TypeOK, Safety
  PROVE  /\ \A o \in Node : Distance(o) = 0 
                 <=> \A n \in Node : counter[o][n] = counter[n][n]
         /\ Measure = 0
            <=> \A v,w,n \in Node : counter[v][n] = counter[w][n]
<1>1. ASSUME NEW o \in Node, Distance(o) = 0, NEW n \in Node
      PROVE  counter[o][n] = counter[n][n]
  BY <1>1, MeasureType, SumIsZero DEF Distance, DistFun, TypeOK, Safety
<1>2. ASSUME NEW o \in Node, \A n \in Node : counter[o][n] = counter[n][n]
      PROVE  Distance(o) = 0
  BY <1>2, MeasureType, SumIsZero DEF Distance, DistFun, TypeOK
<1>3. ASSUME Measure = 0, NEW v \in Node, NEW w \in Node, NEW n \in Node
      PROVE  counter[v][n] = counter[w][n]
  BY <1>1, <1>3, MeasureType, SumIsZero DEF Measure
<1>4. ASSUME \A v,w,n \in Node : counter[v][n] = counter[w][n]
      PROVE  Measure = 0
  BY <1>2, <1>4, MeasureType, SumIsZero DEF Measure
<1>. QED  BY <1>1, <1>2, <1>3, <1>4

\* A Gossip action will never increase the measure.
LEMMA GossipDoesntIncreaseMeasure ==
  ASSUME TypeOK, TypeOK', Safety, Safety',
         [\E n,o \in Node : Gossip(n,o)]_vars
  PROVE  /\ \A v,w \in Node : DistFun(v)'[w] <= DistFun(v)[w]
         /\ \A v \in Node : Distance(v)' <= Distance(v)
         /\ Measure' <= Measure
<1>1. CASE \E n,o \in Node : Gossip(n,o)
  <2>. ASSUME NEW v \in Node, NEW w \in Node
        PROVE  DistFun(v)'[w] <= DistFun(v)[w]
    BY <1>1 DEF Gossip, TypeOK, Safety, DistFun
  <2>. QED  
    BY SumWeaklyMonotonic, MeasureType, MeasureTypePrime, Zenon
       DEF Distance, Measure
<1>2. CASE UNCHANGED vars
  BY <1>2, MeasureType DEF DistFun, Distance, Measure, vars
<1>. QED  BY <1>1, <1>2

\* A non-stuttering Gossip action decreases the measure.
LEMMA GossipDecreasesMeasure ==
  ASSUME TypeOK, TypeOK', Safety, Safety',
         <<\E n,o \in Node : Gossip(n,o)>>_vars
  PROVE  Measure' < Measure
<1>. PICK n \in Node, o \in Node : <<Gossip(n,o)>>_vars
  OBVIOUS
<1>1. PICK v \in Node : counter[o][v] < counter[n][v]
  BY DEF Gossip, vars, TypeOK
<1>2. DistFun(o)'[v] < DistFun(o)[v]
  BY <1>1 DEF Gossip, vars, TypeOK, Safety, DistFun
<1>. QED
  BY <1>2, GossipDoesntIncreaseMeasure, SumStronglyMonotonic,
     MeasureType, MeasureTypePrime, Zenon 
     DEF Distance, Measure

(***************************************************************************)
(* We now prove convergence for the tail of the behavior in which only     *)
(* Gossip actions may occur. For convenience, we define a TLA+             *)
(* specification characterizing this eventual behavior.                    *)
(***************************************************************************)

OGSpec ==
  /\ [](TypeOK /\ Safety)
  /\ [][\E n, o \in Node : Gossip(n,o)]_vars
  /\ [](\A n, o \in Node : WF_vars(Gossip(n,o)))

\* The following is the main liveness theorem. Its proof is quite tedious
\* because of a delicate interplay of predicate and temporal logic reasoning.
THEOREM OGLiveness == OGSpec => <>(\A n, o \in Node : counter[n] = counter[o])
<1>. DEFINE Q == \A n, o \in Node : counter[n] = counter[o]
            P(m) == Measure = m
            L(m) == [](P(m) => <>Q)
<1>1. ASSUME NEW m \in Nat,
             \* must explicitly "box" the following assumption,
             \* otherwise PTL reasoning fails below.
             [](\A k \in 0 .. (m-1) : OGSpec => L(k))
      PROVE  [](OGSpec => L(m))
  <2>. DEFINE OGNext == \E n, o \in Node : Gossip(n,o)
  <2>1. CASE m = 0
    <3>1. TypeOK /\ Safety /\ P(m) => Q
      BY <2>1, MeasureIsZero DEF TypeOK
    <3>. QED  BY <3>1, PTL DEF OGSpec
  <2>2. CASE m > 0
    <3>1. OGSpec => [](P(m) => [](\E k \in 0 .. m : P(k)))
      <4>1. TypeOK /\ Safety /\ P(m) => \E k \in 0 .. m : P(k)
        BY MeasureType
      <4>2. /\ TypeOK /\ Safety /\ TypeOK' /\ Safety' 
            /\ \E k \in 0 .. m : P(k)
            /\ [OGNext]_vars
            => (\E k \in 0 .. m : P(k))'
        BY MeasureTypePrime, GossipDoesntIncreaseMeasure
      <4>. QED  BY <4>1, <4>2, PTL DEF OGSpec
    <3>5. OGSpec => [](P(m) /\ <><<OGNext>>_vars => <> \E k \in 0 .. (m-1) : P(k))
      <4>1. /\ TypeOK /\ Safety /\ TypeOK' /\ Safety'
            /\ \E k \in 0 .. m : P(k)
            /\ <<OGNext>>_vars
            => (\E k \in 0 .. (m-1) : P(k))'
        BY MeasureTypePrime, GossipDecreasesMeasure
      <4>. QED  BY <3>1, <4>1, PTL DEF OGSpec
    <3>6. OGSpec => [](P(m) /\ [][~OGNext]_vars => <> \E k \in 0 .. (m-1) : P(k))
      <4>. DEFINE C(n,o) == counter[o][n] < counter[n][n]
      <4>1. OGSpec /\ [][~OGNext]_vars /\ P(m) => \E u,v \in Node : []C(u,v)
        <5>1. TypeOK /\ Safety /\ P(m) => \E u,v \in Node : C(u,v)
          <6>. SUFFICES ASSUME TypeOK, Safety, P(m)
                        PROVE  \E n,o \in Node : C(n,o)
            OBVIOUS
          <6>1. PICK a,b,c \in Node : counter[a][c] # counter[b][c]
            BY <2>2, MeasureType, MeasureIsZero
          <6>2. CASE counter[a][c] < counter[b][c]
            BY <6>1, <6>2 DEF Safety, TypeOK
          <6>3. CASE counter[b][c] < counter[a][c]
            BY <6>1, <6>3 DEF Safety, TypeOK
          <6>. QED  BY <6>1, <6>2, <6>3 DEF TypeOK
        <5>2. OGSpec /\ [][~OGNext]_vars /\ P(m) => \E u,v \in Node : C(u,v)
          BY <5>1, PTL DEF OGSpec
        <5>3. OGSpec /\ [][~OGNext]_vars => \A u,v \in Node : C(u,v) => []C(u,v)
          <6>. SUFFICES ASSUME NEW u \in Node, NEW v \in Node
                        PROVE  C(u,v) /\ [][OGNext]_vars /\ [][~OGNext]_vars => []C(u,v)
            BY DEF OGSpec
          <6>. C(u,v) /\ [OGNext]_vars /\ [~OGNext]_vars => C(u,v)'
            BY DEF vars
          <6>. QED  BY PTL
        <5>. QED  BY <5>2, <5>3
      <4>2. OGSpec /\ [](\E k \in 0 .. m : P(k)) /\ (\E u,v \in Node : []C(u,v))
            => <> \E k \in 0 .. (m-1) : P(k)
        <5>. SUFFICES 
               ASSUME NEW u \in Node, NEW v \in Node
               PROVE  OGSpec /\ [](\E k \in 0 .. m : P(k)) /\ []C(u,v)
                      => <> \E k \in 0 .. (m-1) : P(k)
          OBVIOUS
        <5>1. TypeOK /\ C(u,v) => ENABLED <<Gossip(u,v)>>_vars
          BY EnabledGossip
        <5>2. /\ TypeOK /\ TypeOK' /\ Safety /\ Safety'
              /\ \E k \in 0 .. m : P(k)
              /\ <<Gossip(u,v)>>_vars
              => (\E k \in 0 .. (m-1) : P(k))'
          BY MeasureTypePrime, GossipDecreasesMeasure
        <5>3. OGSpec => WF_vars(Gossip(u,v))
          <6>1. (\A n,o \in Node : WF_vars(Gossip(n,o))) => WF_vars(Gossip(u,v))
            OBVIOUS
          <6>. QED  BY <6>1, PTL DEF OGSpec
        <5>. QED  BY <5>1, <5>2, <5>3, PTL DEF OGSpec
      <4>. HIDE DEF OGNext, P, C
      <4>3. OGSpec /\ [][~OGNext]_vars /\ P(m) /\ [](\E k \in 0 .. m : P(k))
            => <>(\E k \in 0 .. (m-1) : P(k))
         BY <4>1, <4>2
      <4>. QED  BY <3>1, <4>3, PTL DEF OGSpec
    <3>7. OGSpec => [](P(m) =>  <> \E k \in 0 .. (m-1) : P(k))
      BY <3>5, <3>6, PTL
    <3>8. OGSpec => []((\E k \in 0 .. (m-1) : P(k)) => <>Q)
      <4>1. (\A k \in 0 .. (m-1) : OGSpec => L(k)) 
            => (OGSpec => [](\A k \in 0 .. (m-1) : P(k) => <>Q))
        OBVIOUS
      <4>2. (\A k \in 0 .. (m-1) : P(k) => <>Q) 
            => ((\E k \in 0 .. (m-1) : P(k)) => <>Q)
        OBVIOUS
      <4>. QED  BY <1>1, <4>1, <4>2, PTL
    <3>. QED  BY <3>7, <3>8, PTL
  <2>. QED  BY <2>1, <2>2
<1>. DEFINE S(m) == [](OGSpec => L(m))
\* The following step just commutes [] and \A in the assumption of <1>1
\* so that we can apply the induction theorem in the following step.
<1>2. ASSUME NEW m \in Nat,
             \A k \in 0 .. (m-1) : S(k)
      PROVE  S(m)
  BY <1>1
<1>3. \A m \in Nat : S(m)
  <2>. HIDE DEF S
  <2>. QED  BY <1>2, GeneralNatInduction, Isa
\* Now turn the outermost universal quantifier into an existential quantifier
\* on the left-hand side of the consequent.
<1>4. OGSpec => []((\E m \in Nat : P(m)) => <>Q)
  <2>1. (\A m \in Nat : P(m) => <> Q) => ((\E m \in Nat : P(m)) => <>Q)
    OBVIOUS
  <2>2. [](\A m \in Nat : P(m) => <> Q) => []((\E m \in Nat : P(m)) => <>Q)
    BY <2>1, PTL
  <2>3. ASSUME NEW m \in Nat
        PROVE  OGSpec => L(m)
    <3>1. S(m)
      BY <1>3
    <3>. QED  BY <3>1, PTL
  <2>. QED  BY <1>3, <2>2, <2>3
\* Clearly P(m) must hold for some natural number initially.
<1>5. OGSpec => \E m \in Nat : P(m)
  <2>. TypeOK /\ Safety => \E m \in Nat : P(m)
    BY MeasureType
  <2>. QED  BY PTL DEF OGSpec
<1>. QED  BY <1>4, <1>5, PTL

\* The final theorem is a simple corollary.
THEOREM Liveness == FairSpec => Convergence
<1>1. (\A n,o \in Node : WF_vars(Gossip(n,o))) =>
      [](\A n,o \in Node : WF_vars(Gossip(n,o)))
  \* Tedious proof of an "obvious" fact, due to interplay of first-order 
  \* and temporal reasoning. Could this be proved automatically?
  <2>1. ASSUME NEW n \in Node, NEW o \in Node
        PROVE  WF_vars(Gossip(n,o)) => []WF_vars(Gossip(n,o))
    BY PTL
  <2>. QED  BY <2>1, Isa
<1>. QED
  BY <1>1, TypeCorrect, Safe, OGLiveness, PTL 
     DEF FairSpec, OGSpec, Fairness, Convergence

=============================================================================

@lemmy
Copy link
Member Author

lemmy commented Feb 12, 2025

Closed in favor of #155

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants